//
//  DCDownloadOprationManager.m
//  DCDownloadOpration
//
//  Created by Wade on 16/3/30.
//  Copyright © 2016年 Wade. All rights reserved.
//

#import "DCDownloadOprationManager.h"
#import "DCDownloadOpration.h"
#import "NSString+DCDownload.h"
#import "DCDownloadConst.h"



@interface DCDownloadOprationManager()<NSURLSessionDataDelegate>
/** 回调的队列 */
@property (strong, nonatomic) NSOperationQueue *queue;

/** session */
@property (strong, nonatomic) NSURLSession *session;

/** 存放所有文件的下载信息 */
@property (strong, nonatomic) NSMutableArray *downloadInfoArray;
/** 是否正在批量处理 */
@property (assign, nonatomic, getter=isBatching) BOOL batching;

/**
 *  正在下载的Opration
 */
@property (nonatomic, strong) NSMutableArray *downloadingArr;
/**
 *  已经下载的opration
 */
@property (nonatomic, strong) NSMutableArray *downloadedArr;
@end

@implementation DCDownloadOprationManager

/** 存放所有的manager */
static NSMutableDictionary *_managers;
/** 锁 */
static NSRecursiveLock *_lock;

#pragma Manager初始化

-(NSMutableArray *)downloadingArr{
    if (!_downloadingArr) {
        _downloadingArr = [NSMutableArray array];
    }
    return _downloadingArr;
    
}

- (NSMutableArray *)downloadedArr{
    if (!_downloadedArr) {
        _downloadedArr = [NSMutableArray array];
    }
    return _downloadedArr;
}

+ (void)initialize
{
    _totalFileSizesFile = [[NSString stringWithFormat:@"%@/%@", DCDownloadRootDir, @"MJDownloadFileSizes.plist".MD5] prependCaches];
    
    _totalFileSizes = [NSMutableDictionary dictionaryWithContentsOfFile:_totalFileSizesFile];
    if (_totalFileSizes == nil) {
        _totalFileSizes = [NSMutableDictionary dictionary];
    }
    
    _managers = [NSMutableDictionary dictionary];
    
    _lock = [[NSRecursiveLock alloc] init];
}


+ (instancetype)manager
{
    return [[self alloc] init];
}

// 保证同一个identifier manager只会创建被创建一次
+ (instancetype)managerWithIdentifier:(NSString *)identifier
{
    if (identifier == nil) return [self manager];
    
    DCDownloadOprationManager *mgr = _managers[identifier];
    if (!mgr) {
        mgr = [self manager];
        _managers[identifier] = mgr;
    }
    mgr.maxDownloadingCount = 3;
    return mgr;
}

#pragma 任务管理
- (NSURLSession *)session
{
    if (!_session) {
        // 配置
        NSURLSessionConfiguration *cfg = [NSURLSessionConfiguration defaultSessionConfiguration];
        // session
        self.session = [NSURLSession sessionWithConfiguration:cfg delegate:self delegateQueue:self.queue];
    }
    return _session;
}

- (NSOperationQueue *)queue
{
    if (!_queue) {
        self.queue = [[NSOperationQueue alloc] init];
        self.queue.maxConcurrentOperationCount = 2;
    }
    return _queue;
}

- (void)setMaxDownloadingCount:(int)maxDownloadingCount
{
    if (maxDownloadingCount <= 0) {
        maxDownloadingCount = 1;
    }
    _maxDownloadingCount = maxDownloadingCount;
    self.queue.maxConcurrentOperationCount = maxDownloadingCount;
}

- (NSMutableArray *)downloadInfoArray
{
    if (!_downloadInfoArray) {
        self.downloadInfoArray = [NSMutableArray array];
    }
    return _downloadInfoArray;
}


/**
 * 让第一个等待下载的文件开始下载
 */
- (void)resumeFirstWillResume
{
    //    if (self.isBatching) return;
    //    NSArray *waitingArr = [self.downloadInfoArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"state==%d", DCDownloadStateWaiting]];
    
    for (DCDownloadOpration  *opration in self.downloadInfoArray)
    {
//        NSLog(@"resumeFirstWillResumestate:任务状态--%d",opration.state);
        
        if (opration.state == DCDownloadStateWaiting)
        {
            [self resume:opration.url];
            if (self.downloadingArr.count >= self.maxDownloadingCount) {
                return;
            }
            else{
                continue;
            }
        }
    }
    
}

- (void)cancelAll
{
    [self.downloadInfoArray enumerateObjectsUsingBlock:^(DCDownloadOpration *opration, NSUInteger idx, BOOL *stop) {
        [self cancel:opration.url];
    }];
}

+ (void)cancelAll
{
    [_managers.allValues makeObjectsPerformSelector:@selector(cancelAll)];
}

- (void)suspendAll
{
    self.batching = YES;
    [self.downloadInfoArray enumerateObjectsUsingBlock:^(DCDownloadOpration *opration, NSUInteger idx, BOOL *stop) {
        [self suspend:opration.url];
    }];
    self.batching = NO;
}

+ (void)suspendAll
{
    [_managers.allValues makeObjectsPerformSelector:@selector(suspendAll)];
}

- (void)resumeAll
{
    [self.downloadInfoArray enumerateObjectsUsingBlock:^(DCDownloadOpration *opration, NSUInteger idx, BOOL *stop) {
        [self resume:opration.url];
    }];
}

+ (void)resumeAll
{
    [_managers.allValues makeObjectsPerformSelector:@selector(resumeAll)];
}

- (void)cancel:(NSString *)url
{
    if (url == nil) return;
    
    // 取消
    [[self downloadInfoForURL:url] cancel];
    
    // 这里不需要取出第一个等待下载的，因为调用cancel会触发-URLSession:task:didCompleteWithError:
    [self resumeFirstWillResume];
}

- (void)suspend:(NSString *)url
{
    if (url == nil) return;
    
    // 暂停
    [[self downloadInfoForURL:url] suspend];
    
    // 取出第一个等待下载的
    [self resumeFirstWillResume];
}

/**
 *  根据URL重启任务
 *
 *  @param url state = waiting的Opration URL
 */
- (void)resume:(NSString *)url
{
    if (url == nil) return;
    
    // 获得下载信息
    DCDownloadOpration *opration = [self downloadInfoForURL:url];
    
    // 正在下载的
    if (self.downloadingArr.count == self.maxDownloadingCount) {
        // 等待下载
        [opration willResume];
//        NSLog(@"等待任务--%@--%d",opration.url,opration.state);
    } else { // 否则开始下载
        // 继续
        [opration resume];
//        NSLog(@"开始任务--%@--%d",opration.url,opration.state);
        if(![self.downloadingArr containsObject:opration])
            [self.downloadingArr addObject:opration];
    }
}

#pragma mark 公共方法
/**
 *  根据URL 生成downloadopration信息
 *
 */
- (DCDownloadOpration *)downloadInfoForURL:(NSString *)url {
    if (url == nil) return nil;
    // 利用一个数组的数据来过滤另一个数组
    // 先查询下载列表有没有该任务如果有直接返回该对象  如果没有创建新的任务添加到数组中
    // 利用临时变量修复was mutated while being enumerated crash的bug
    NSMutableArray *temp = [self.downloadInfoArray mutableCopy];
    DCDownloadOpration *opration = [temp filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"url==%@", url]].firstObject;
    
    if (opration == nil)
    {
        opration = [[DCDownloadOpration alloc] init];
        opration.url = url; // 设置url
        opration.state = DCDownloadStateWaiting;
        [self.downloadInfoArray addObject:opration];
        
//        NSLog(@"添加任务--%@--%d",opration.url,opration.state);
    }
    return opration;
}

#pragma mark - 公共方法
- (DCDownloadOpration *)download:(nonnull NSString *)url toDestinationPath:(nonnull NSString *)destinationPath progress:(DCDownloadOprationResuming)progress state:(DCDownloadStateChangeBlock)statechange
{
    // 下载信息
    DCDownloadOpration *opration = [self downloadInfoForURL:url];
    
    // 如果已经下载完毕
    if (opration.state == DCDownloadStateCompleted ||opration.state == DCDownloadStateFail) //如果下载完成
    {
        // 完毕
//        NSLog(@"下载完成--%@--%d",opration.url,opration.state);
        [opration notifyStateChange];
        [self resumeFirstWillResume];
        return opration;
    } else if (opration.state == DCDownloadStateResumed) // 如果正在下载
    {
        [opration notifyProgressChange];
        return opration;
    }else{ // 如果不是正在下载的任务 添加到任务队列
        // 创建任务
        // 设置保存路径
        opration.destinationPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:destinationPath];;
        
        // 设置block
        opration.downloadResumingBlock = progress;
        opration.downloadStateChangeBlock = statechange;
        
        // 设置opration的session
        [opration setupTask:self.session];
        if (self.downloadingArr.count <= self.maxDownloadingCount || [self.downloadingArr containsObject:opration]) {
            // 开始任务
            [self resume:url];
            if (![self.downloadingArr containsObject:opration]) {
                [self.downloadingArr addObject:opration];
            }
            
        }
        return opration;
    }
}

- (DCDownloadOpration *)download:(NSString *)url progress:(DCDownloadOprationResuming)progress state:(DCDownloadStateChangeBlock)state
{
    NSString *destPath = [DCDefaultCachePath stringByAppendingPathComponent:[url lastPathComponent]];
    return [self download:url toDestinationPath:destPath progress:progress state:state];
}

- (DCDownloadOpration *)download:(NSString *)url state:(DCDownloadStateChangeBlock)state
{
    NSString *destPath = [DCDefaultCachePath stringByAppendingPathComponent:[url lastPathComponent]];
    return [self download:url toDestinationPath:destPath progress:nil state:state];
}

- (DCDownloadOpration *)download:(NSString *)url
{
    NSString *destPath = [DCDefaultCachePath stringByAppendingPathComponent:[url lastPathComponent]];
    return [self download:url toDestinationPath:destPath progress:nil state:nil];
}


#pragma mark - <NSURLSessionDataDelegate>
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
    // 获得下载信息
    DCDownloadOpration *opration = [self downloadInfoForURL:dataTask.taskDescription];
    
    // 处理响应
    [opration didReceiveResponse:response];
    
    // 继续
    completionHandler(NSURLSessionResponseAllow);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
    //     获得下载信息
    DCDownloadOpration *opration = [self downloadInfoForURL:dataTask.taskDescription];
    if (data) {
        opration.state = DCDownloadStateResumed;
        // 处理数据
        [opration didReceiveData:data];
    }
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    // 获得下载信息
    DCDownloadOpration *opration = [self downloadInfoForURL:task.taskDescription];
    [opration didCompleteWithError:error];
    
    [self.downloadInfoArray removeObject:opration];
    [self.downloadingArr removeObject:opration];
    [self.downloadedArr addObject:opration];
    
    // 再恢复等待下载的
    [self resumeFirstWillResume];
}

@end
